/*
//              INTEL CORPORATION PROPRIETARY INFORMATION
//  This software is supplied under the terms of a license  agreement or
//  nondisclosure agreement with Intel Corporation and may not be copied
//  or disclosed except in  accordance  with the terms of that agreement.
//    Copyright (c) 2007 Intel Corporation. All Rights Reserved.
//
*/

#include "umc_defs.h"
#if defined (UMC_ENABLE_DV_VIDEO_ENCODER)

#include "umc_dv_enc_compressor_def.h"
#include "umc_dv_enc_segment_compressor.h"
#include "umc_dv_enc_encode_bit_stream.h"
#include "umc_dv_enc_block.h"
#include "umc_dv_enc_huffman.h"
#include <ipps.h>

namespace UMC
{

static Ipp32s DV_DCTBlockLengths[6] = {14, 14, 14, 14, 10, 10};
static Ipp32s DV100_DCTBlockLengths[8] = {10, 10, 10, 10, 10, 10, 8, 8};

SegmentCompressor::SegmentCompressor():
    m_lpbBuffer(NULL),
    m_lpbBitSequences(NULL),
    m_Streams(NULL),
    m_DCTBlocksBufMID(0),
    m_BitSeqBufMID(0),
    m_bQuantPredictor(0)
{
}

SegmentCompressor::~SegmentCompressor()
{
    ReleaseCompressor();
}

Status SegmentCompressor::InitCompressor(int iDCTBlocksInMB, MemoryAllocator *pMemoryAllocator)
{
    Ipp32s TotalDCTBlocks = 5 * iDCTBlocksInMB;

    ReleaseCompressor();

    m_pMemoryAllocator = pMemoryAllocator;

    m_nDCTBlocksInMB = iDCTBlocksInMB;
    m_DCTBlockLengths = (m_nDCTBlocksInMB == 6) ? DV_DCTBlockLengths : DV100_DCTBlockLengths;

    //Initialize Video Segment

    //  We need 30(40) blocks of Ipp16s:
    //  5 macro block consisting of 6(8) blocks.
    //  Each block has 64 unit. We also
    //  get memory for mblock.
    //  And we will store signs in Ipp16s arrays
    //  with constant offset from data.

    if( UMC_OK != m_pMemoryAllocator->Alloc(&m_DCTBlocksBufMID,
                                            sizeof(Ipp16s) * 64 * TotalDCTBlocks + sizeof(BLOCK) * TotalDCTBlocks + 4096*2,
                                            UMC_ALLOC_PERSISTENT) )
        return UMC_ERR_ALLOC;


    //Allocate memory for the bit sequences
    if( UMC_OK != m_pMemoryAllocator->Alloc( &m_BitSeqBufMID,
                                             128 * TotalDCTBlocks + sizeof(ENCODE_BIT_STREAM) * TotalDCTBlocks,
                                             UMC_ALLOC_PERSISTENT) )
        return UMC_ERR_ALLOC;

    return UMC_OK;

} //Status SegmentCompressor::InitCompressor()

void SegmentCompressor::LockWorkBuffers()
{
    Ipp32s TotalDCTBlocks = 5 * m_nDCTBlocksInMB, nDCTBlockNum;
    Ipp8s *lpc;

    m_VSegment.m_lpcBuffer = (Ipp8s*)m_pMemoryAllocator->Lock(m_DCTBlocksBufMID);

    lpc = m_VSegment.m_lpcBuffer + 4096*2;

    m_VSegment.m_pDCTBlocks = (BLOCK*)(lpc + sizeof(Ipp16s) * 64 * TotalDCTBlocks);
    m_VSegment.m_pDCTBlocks[0].m_lpsDataRL = (Ipp32u*)m_VSegment.m_lpcBuffer;

    for( nDCTBlockNum=0; nDCTBlockNum < TotalDCTBlocks; nDCTBlockNum++)
    {
        m_VSegment.m_pDCTBlocks[nDCTBlockNum].m_lpsData = (Ipp16s *) lpc;
        lpc += sizeof(Ipp16s) * 64;
    }


    m_lpbBuffer = (Ipp8u*)m_pMemoryAllocator->Lock(m_BitSeqBufMID);
    m_lpbBitSequences = m_lpbBuffer;
    m_Streams = (ENCODE_BIT_STREAM *) (m_lpbBuffer + 128 * TotalDCTBlocks);
}

void SegmentCompressor::UnlockWorkBuffers()
{
    m_pMemoryAllocator->Unlock(m_DCTBlocksBufMID);
    m_VSegment.m_lpcBuffer = NULL;
    m_VSegment.m_pDCTBlocks = NULL;

    m_pMemoryAllocator->Unlock(m_BitSeqBufMID);
    m_lpbBuffer = NULL;
    m_lpbBitSequences = NULL;
    m_Streams   = NULL;
}

void SegmentCompressor::CompressSegment(DV_SEGMENT *lpDVSegment)
{
    ResetEncodeBitStream();

    DoDCT();

    ZigZagData();

    QuantizeData(lpDVSegment);

    EncodeHuffman(lpDVSegment);

} // void CompressSegment(...)

void SegmentCompressor::ReleaseCompressor()
{
    if( m_DCTBlocksBufMID )
        m_pMemoryAllocator->DeallocateMem( m_DCTBlocksBufMID );
    m_DCTBlocksBufMID = 0;
    m_VSegment.m_lpcBuffer = NULL;
    m_VSegment.m_pDCTBlocks = NULL;

    if( m_BitSeqBufMID )
        m_pMemoryAllocator->DeallocateMem( m_BitSeqBufMID );
    m_BitSeqBufMID = 0;
    m_lpbBuffer = NULL;
    m_lpbBitSequences = NULL;
    m_Streams   = NULL;

    m_pMemoryAllocator = NULL;

} // void ReleaseSegmentCompressor(SEGMENT_COMPRESSOR *lpSCompressor)


void SegmentCompressor::ResetEncodeBitStream()
{
    Ipp32s MBNum, DCTBlockNum;
    ENCODE_BIT_STREAM *lpStream;
    Ipp8u *lpbDst;

    lpStream = m_Streams;
    lpbDst = m_lpbBitSequences;

    // do initialization
    for (MBNum = 0; MBNum < 5; MBNum++)
    {
        for(DCTBlockNum = 0; DCTBlockNum < m_nDCTBlocksInMB; DCTBlockNum++)
        {
            lpStream->m_lpcDest = lpbDst;
            lpStream->m_bBitsCanTake = m_DCTBlockLengths[DCTBlockNum] * 8;
            lpStream->m_bFreeBits = 32; //free bits in register

            lpStream++;
            lpbDst += 128;
        }
    }
}

}//namespace UMC

#endif //(UMC_ENABLE_DV_VIDEO_ENCODER)
